﻿Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Windows.Data
Imports System.Data
Imports Sybase.Data.AseClient
Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Windows.Media
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Reflection
Imports System.Globalization
Imports System.Security.Permissions

Class FLResumenBSO

    Private lstDataPartida As New ObservableCollection(Of CTPartidaSolicitud)
    Private partida As CTPartidaSolicitud
    Private lstCatalogoTVNG As New List(Of CTCatalogoGenerico)
    Private lstFondosSaldos As New List(Of CTSaldoFondosPS)
    Private lstRemanenteInter As New List(Of CTRemanentesInterPA)
    Private fechasInhabiles As List(Of CFechaNoLab) 
    Private tituloMensaje As String = "Boleta de Solicitud de Operaciones"
    Private maintainsession As String
    Private servicioGL As CSGeneralGL
    Private servicioGenerales As CSServiciosGenerales
    Private servicioGeneral As CServiciosGenerales

    Public Sub New()
        servicioGL = New CSGeneralGL()
        servicioGenerales = New CSServiciosGenerales()
        servicioGeneral = New CServiciosGenerales()
        fechasInhabiles = servicioGeneral.ObtenerFechasNoLab(1)
        Dim catalogo = New CTCatalogoGenerico()
        lstCatalogoTVNG = catalogo.ObtieneCatalogo(5)
        InitializeComponent()
        CFLNotificacionDB.CreatePermission()
        Dim modelo As CTModelMsgNotificacion = New CTModelMsgNotificacion(Me.Dispatcher, hilo)              
        AddHandler modelo.PropertyChanged,AddressOf CountMsg_PropertyChanged

        Mouse.OverrideCursor = Nothing
    End Sub


    Private Sub CountMsg_PropertyChanged(sender As Object, e As PropertyChangedEventArgs)
        If  hilo > 0 AND CType( sender,CTModelMsgNotificacion).numHilo = (hilo - 1) then      
            BSO.CargarBSOActual()
            InicioTabResumenBSO()   
        End If
    End Sub

    Private Sub tabContenedor_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
        Try
            If (e.OriginalSource.GetType Is GetType(TabControl)) Then
                Dim objCurrentSelectedTabItem As TabItem
                If (e.AddedItems IsNot Nothing AndAlso e.AddedItems.Count > 0) Then
                    objCurrentSelectedTabItem = e.AddedItems(0)
                    If (objCurrentSelectedTabItem IsNot Nothing) Then

                        If (objCurrentSelectedTabItem.Name = tabResumenBSO.Name) Then
                            InicioTabResumenBSO()
                        End If
                        If (objCurrentSelectedTabItem.Name = tabPartidaSolicitud.Name) Then
                            InicioTabPartidaSolicitud()
                        End If
                        If (objCurrentSelectedTabItem.Name = tabPartidaAjuste.Name) Then
                            InicioTabPartidaAjuste()
                        End If

                        If (objCurrentSelectedTabItem.Name = tabModificarPartidaS.Name) Then
                            InicioTabModificarPS()
                        End If
                        If (objCurrentSelectedTabItem.Name = tabModificarPartidaA.Name) Then
                            InicioTabModificarPA()
                        End If

                    End If
                End If

            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Sub ReinicioTab()
        Me.tabContenedor.SelectedIndex = 0
        Me.tabContenedor.SelectedItem = Me.tabResumenBSO
        Me.tabResumenBSO.IsSelected = True
    End Sub

#Region "TAB RESUMEN BSO"
    Private Sub InicioTabResumenBSO()
        Mouse.OverrideCursor = Cursors.Wait
        Try
            lstDataPartida.Clear()
            Dim resumen As CTPartidaSolicitud
            Dim lstPS As List(Of CTPartidaSolicitud) = BSO.LstPS'.FindAll(Function(x) x.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.CANCELADA and x.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.CANCELADA_ACEPTADA)
            Dim lstPE As List(Of CTPartidaEJ)
            Dim lstPA As List(Of CTPartidaAjuste)
            For Each partida As CTPartidaSolicitud In lstPS
                resumen = New CTPartidaSolicitud()
                resumen.TipoPartida = "Solicitud"
                resumen.FecSol = partida.FecSol
                resumen.FecLiq = partida.FecLiq
                resumen.Tvng = partida.Tvng
                resumen.Plazo = partida.Plazo
                resumen.CantidadSol = partida.CantidadSol
                resumen.EstatusActualPS = partida.EstatusActualPS
                resumen.Estatus = partida.Estatus
                resumen.Comentarios = partida.Comentarios
                resumen.CantidadActualPS = partida.CantidadActualPS
                resumen.IntermFlexibles = partida.IntermFlexibles
                resumen.LstPEJ = partida.LstPEJ
                resumen.PartidaSolId = partida.PartidaSolId
                resumen.Agrupador = partida.SecSol.ToString() + ".- " + resumen.Tvng + Chr(9) + Chr(9) + Chr(9) + Chr(9) + Chr(9) + resumen.CantidadActualPS.ToString(" ##,##0.00")
                lstDataPartida.Add(resumen)
                lstPE = partida.LstPEJ'.FindAll(Function(x) x.EstatusPEJ.EstatusDist <> CTEstadoPEJ.enumEstatusPEJ.CANCELADA)
                For Each ejecucion As CTPartidaEJ In lstPE
                    lstPA = ejecucion.LstPAJ'.FindAll(Function(x) x.Estatus <> CTPartidaAjuste.enumEstatusPAJ.CANCELADA and x.Estatus <> CTPartidaAjuste.enumEstatusPAJ.CANCELADA_ACEPTADA)
                    For Each partidaAj As CTPartidaAjuste In lstPA
                        resumen = New CTPartidaSolicitud()
                        resumen.TipoPartida = "Ajuste"
                        resumen.FecLiq = partida.FecLiq
                        resumen.FecSol = partida.FecSol
                        resumen.Tvng = partida.Tvng
                        resumen.Plazo = partida.Plazo
                        resumen.CantidadSol = partidaAj.CantidadAjus
                        resumen.EstatusActualPS = partidaAj.Estatus
                        resumen.Estatus = partidaAj.EstatusValor
                        resumen.Comentarios = partidaAj.Comentario
                        resumen.CantidadActualPS = partida.CantidadActualPS
                        resumen.PartidaSolId = partidaAj.PartidaAjusteID
                        resumen.AncestroPS = partidaAj.PartidaSolicitudId
                        resumen.Agrupador = partida.SecSol.ToString() + ".- " + resumen.Tvng + Chr(9) + Chr(9) + Chr(9) + Chr(9) + Chr(9) + resumen.CantidadActualPS.ToString(" ##,##0.00")
                        lstDataPartida.Add(resumen)
                    Next
                Next
            Next

        Catch ex As Exception
            Mouse.OverrideCursor = Nothing
            MessageBox.Show("Error TI500. Error al cargar partidas: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
        End Try
        Dim lstResumenBSO = New ListCollectionView(lstDataPartida)
        lstResumenBSO.GroupDescriptions.Add(New PropertyGroupDescription("Agrupador"))
        Me.grdResumenBSO.ItemsSource = lstResumenBSO
        ModificaFechaBSO()
        Mouse.OverrideCursor = Nothing
    End Sub

    Public Function espacios (ByVal cadena As String, ByVal cantidad As Integer) As String
        Dim cadenamod As String = cadena
        Dim cantespacios As Integer = cantidad - cadena.Length
        cadenamod = cadenamod + Space(cantespacios)
        Return cadenamod
    End Function

    Private Sub btnCancelar_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnCancelar.Click
        Mouse.OverrideCursor = Cursors.Wait
        Dim valido = ValidarCancelaciones()
        If valido Then
            If MessageBox.Show ("¿Desea cancelar las partidas seleccionadas?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
                Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.CANCELAR_PS_PAJ, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnCancelar")
                Try
                    Dim psIds = New List(Of Long)
                    Dim paIds = New List(Of Long)
                    Dim lst = grdResumenBSO.ItemsSource
                    For Each resumen As CTPartidaSolicitud In lst
                        If (resumen.SeleccionSol) Then
                            If resumen.TipoPartida = "Solicitud" Then
                                eventoBit.GuadarDetalle("idPS", resumen.PartidaSolId) 
                                BSO.CancelarPS(resumen.PartidaSolId)
                            Else
                                eventoBit.GuadarDetalle("idPAJ", resumen.PartidaSolId)
                                BSO.CancelarPAJ(resumen.PartidaSolId, resumen.Estatus)
                            End If

                        End If
                    Next
                    servicioGenerales.RegistrarNotificacion("Resumen BSO - Se cancela partidas")
                    eventoBit.ActualizarFechaFinDelEvento()
                    MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                
                Catch ex As Exception
                    Dim errorBit = eventoBit.GuardarError("Error al cancelar PS - PAJ: " & ex.Message)
                    eventoBit.ActualizarFechaFinDelEvento()
                    MessageBox.Show("Error TI500. Error al cancelar partidas: " & ex.Message & " -Codigo:" & errorBit.IdError, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                    Mouse.OverrideCursor = Nothing
                End Try
                
                
            End If
        End If
        Mouse.OverrideCursor = Nothing
    End Sub

    Private Function ValidarCancelaciones() As Boolean
        Dim valido As Boolean = True
        Dim count As Integer = 0
        Dim countEstatus As Integer = 0
        Dim countAj As Integer = 0
        Dim countPA As Integer = 0
        Dim countSelPA As Integer = 0
        Dim lst = grdResumenBSO.ItemsSource
        For Each resumen As CTPartidaSolicitud In lst
            If (resumen.SeleccionSol) Then
                count = count + 1
                If resumen.Estatus <> "CREADA" And resumen.Estatus <> "ENVIADA" Then
                    countEstatus = countEstatus + 1
                End If
                If resumen.TipoPartida = "Ajuste" and resumen.Estatus <> "CREADA" Then
                    countAj = countAj + 1
                End If
            End If
        Next
        If count = 0 Then
            MessageBox.Show ("Se debe seleccionar al menos una partida.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If
        If valido and countAj > 0 Then
            MessageBox.Show ("No se puede cancelar partidas de Ajuste distintas a CREADA.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If
        If valido and countEstatus > 0 Then
            MessageBox.Show ("No se puede cancelar partidas con estatus distinto a CREADA o ENVIADA.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If
        
 
        Return valido
    End Function

    Private Sub btnLiberar_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnLiberar.Click
        Mouse.OverrideCursor = Cursors.Wait
        Dim valido = ValidarLiberaciones()
        If valido Then
            If MessageBox.Show ("¿Desea enviar las partidas seleccionadas?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
                Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.LIBERAR_PS_PAJ, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnLiberar")
                Try
                    Dim psIds = New List(Of Long)
                    Dim paIds = New List(Of Long)
                    Dim lst = grdResumenBSO.ItemsSource
                    For Each resumen As CTPartidaSolicitud In lst
                        If (resumen.SeleccionSol) Then
                            If resumen.TipoPartida = "Solicitud" Then
                                eventoBit.GuadarDetalle("idPS", resumen.PartidaSolId) 
                                BSO.EnviarPS(resumen.PartidaSolId)
                            Else
                                eventoBit.GuadarDetalle("idPAJ", resumen.PartidaSolId) 
                                BSO.EnviarPAJ(resumen.PartidaSolId)
                            End If

                        End If
                    Next
                    eventoBit.ActualizarFechaFinDelEvento()
                    servicioGenerales.RegistrarNotificacion("Resumen BSO - Se envia partida")
                    MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                Catch ex As Exception
                    Dim errorBit = eventoBit.GuardarError("Error al liberar PS - PAJ: " & ex.Message)
                    eventoBit.ActualizarFechaFinDelEvento()
                    MessageBox.Show("Error TI500. Error al enviar partidas: " & ex.Message & " -Codigo:" & errorBit.IdError, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                    Mouse.OverrideCursor = Nothing
                End Try
                
                
            End If
        End If
        Mouse.OverrideCursor = Nothing

    End Sub

    Private Function ValidarLiberaciones() As Boolean
        Dim valido As Boolean = True
        Dim count As Integer = 0
        Dim countEstatus As Integer = 0
        
        Dim lst = grdResumenBSO.ItemsSource
        For Each resumen As CTPartidaSolicitud In lst
            If (resumen.SeleccionSol) Then
                count = count + 1
                If resumen.Estatus <> "CREADA" Then
                    countEstatus = countEstatus + 1
                End If
            End If
        Next
        If count = 0 Then
            MessageBox.Show ("Se debe seleccionar al menos una partida.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If
        If countEstatus > 0 Then
            MessageBox.Show ("Solo se puede enviar partidas con estatus igual a CREADA.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If
       
        Return valido
    End Function

    Private Sub chkSelectAll_Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
       For Each resumen As CTPartidaSolicitud In lstDataPartida
            resumen.SeleccionSol = True
        Next
        Dim lstResumenBSO = New ListCollectionView(lstDataPartida)
        lstResumenBSO.GroupDescriptions.Add(New PropertyGroupDescription("Agrupador"))
        Me.grdResumenBSO.ItemsSource = lstResumenBSO

    End Sub

    Private Sub chkSelectAll_Unchecked(ByVal sender As Object, ByVal e As RoutedEventArgs)
        For Each resumen As CTPartidaSolicitud In lstDataPartida
            resumen.SeleccionSol = False
        Next
        Dim lstResumenBSO = New ListCollectionView(lstDataPartida)
        lstResumenBSO.GroupDescriptions.Add(New PropertyGroupDescription("Agrupador"))
        grdResumenBSO.ItemsSource = lstResumenBSO
    End Sub

    Private Sub btnCerrar_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnCerrar.Click
        Mouse.OverrideCursor = Cursors.Wait
        If ValidarCierre() Then
            If MessageBox.Show ("¿Desea cerra la Boleta de Solicitud de Operaciones?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
                Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.CERRAR_BSO, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnCerrar")
                Try
                    Dim fechaApertura = GetSiguienteDiaHabil(BSO.FecOperacion)
                    BSO.CerrarBSO(fechaApertura, eventoBit)
                    eventoBit.ActualizarFechaFinDelEvento()
                    servicioGenerales.RegistrarNotificacion("Resumen BSO - Se cierra la boleta de solicitud de operaciones")
                    MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                Catch ex As Exception
                    eventoBit.ActualizarFechaFinDelEvento()
                    Mouse.OverrideCursor = Nothing
                    MessageBox.Show("Error TI500. Error al cerrar la boleta de solicitud de operaciones: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                End Try
                
                
            End If
        End If
        Mouse.OverrideCursor = Nothing
    End Sub

    Private Function GetSiguienteDiaHabil(ByVal fecha As DateTime) As DateTime
        Dim flag As Boolean = False
        While Not flag
            fecha = fecha.AddDays(1)
            flag = ValidaFechaHabil(fecha)
        End While
        Return fecha
    End Function

    Private Function ValidarCierre() As Boolean
        Dim valido As Boolean = True
        'If BSO.LstPS.FindAll(Function(x) x.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.CREADA _
        '                           or x.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.ENVIADA _
        '                           or x.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.EN_PROCESO _
        '                           or x.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.LIBERADA _
        '                           or x.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.EN_DISTRIBUCION).Count() > 0 Then

        '    MessageBox.Show ("Solo se puede cerrar la Boleta si todas sus solicitudes estan cerradas.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
        '    Return False
        'End If
        If BSO.LstPS.FindAll(Function(x) x.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.EN_PROCESO).Count() > 0 Then

            MessageBox.Show ("No se puede cerrar la Boleta porque existen Solicitudes en PROCESO.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            Return False
        End If
        Return valido
    End Function

    Private Sub ModificaFechaBSO()
        fechaBSO.Content = "Fecha Aplicación BSO : "
        If (BSO.EstatusBSOID = CTBSO.enumEstatusBSO.ABIERTA) Then
            fechaBSO.Content = "Fecha Aplicación BSO : " + BSO.FecOperacion.ToString("dd/MM/yyyy")
        End If
    End Sub

#End Region

#Region "TAB PARTIDA SOLICITUD"

    Private Sub InicioTabPartidaSolicitud()
        Mouse.OverrideCursor = Cursors.Wait
        Me.dtpFechaSolicitud.Text = BSO.FecOperacion.ToString("dd/MM/yyyy")
        Me.dtpFechaLiquidacion.Text = BSO.FecOperacion.ToString("dd/MM/yyyy")
        Me.cmbTipoSolicitud.ItemsSource = lstCatalogoTVNG
        Me.cmbTipoSolicitud.SelectedItem = Nothing
        lstFondosSaldos.Clear()
        If ValidaFechaHabil(BSO.FecOperacion.AddDays(1)) Then
            Me.txtPlazo.Text = 1
        Else
            Me.txtPlazo.Text = GetNumSiguienteDiaHabil(BSO.FecOperacion)
        End If
        
        Me.grdSaldoFondos.ItemsSource = lstFondosSaldos
        Me.txtComentario.Clear()
        Mouse.OverrideCursor = Nothing
    End Sub

    Private Sub grdSaldoFondos_CellEditEnding(ByVal sender As System.Object, ByVal e As System.Windows.Controls.DataGridCellEditEndingEventArgs) Handles grdSaldoFondos.CellEditEnding
        Dim txtContenidoCelda As Object
        Try
            If e.EditAction <> DataGridEditAction.Cancel Then
                If Not sender.CurrentItem Is Nothing Then
                    txtContenidoCelda = sender.Columns(e.Column.DisplayIndex).GetCellContent(CType(e.Row.Item, CTSaldoFondosPS))

                    If Not IsNumeric(txtContenidoCelda.Text) Then
                        CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                    Else
                        If Convert.ToDouble(txtContenidoCelda.Text) < 0 Then
                            CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                        End If
                    End If
                        
                Else
                    CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                End If
            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Sub cmbTipoSolicitud_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
        Mouse.OverrideCursor = Cursors.Wait
        Try
            Dim strFecha = (DateTime.Parse(Me.dtpFechaSolicitud.Text)).ToString("yyyy-MM-dd")
            Dim tipoSolicitud As CTCatalogoGenerico
            tipoSolicitud = Me.cmbTipoSolicitud.SelectedItem
            lstFondosSaldos = New List(Of CTSaldoFondosPS)
            If (tipoSolicitud IsNot Nothing) Then
                lstFondosSaldos = servicioGL.ObtieneFondosSaldos(tipoSolicitud.IdCat, tipoSolicitud.Clave, strFecha)
            
                For Each fondo As CTSaldoFondosPS In lstFondosSaldos
                    AddHandler fondo.PropertyChanged, AddressOf FondoSaldo_PropertyChanged
                Next
            End If
            Me.grdSaldoFondos.ItemsSource = lstFondosSaldos
            CargaTotales()

        Catch ex As Exception
            Mouse.OverrideCursor = Nothing
            MessageBox.Show("Error TI500. Error al cargar posicion de fondos: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
        
        Mouse.OverrideCursor = Nothing
    End Sub
 
    Private Sub CargaTotales()
        Dim SaldoTotal As Double
        Dim SaldoTotalSinCortos As Double
        Dim CargosTotal As Double
        Dim SaldoTotalNeto As Double
        Dim MontoSolicitado As Double

        SaldoTotal = 0
        SaldoTotalSinCortos = 0
        CargosTotal = 0
        For Each fondo As CTSaldoFondosPS In lstFondosSaldos
            SaldoTotal = SaldoTotal + fondo.Saldo
            If (fondo.Saldo > 0) Then
                SaldoTotalSinCortos = SaldoTotalSinCortos + fondo.Saldo

            End If
            CargosTotal = CargosTotal + fondo.Cargos
        Next

        SaldoTotalNeto = SaldoTotalSinCortos - CargosTotal
        MontoSolicitado = SaldoTotalNeto

        Me.txtSaldoTotal.Text = SaldoTotal.ToString(" ##,##0.00")
        Me.txtSaldoTotalSinCortos.Text = SaldoTotalSinCortos.ToString(" ##,##0.00")
        Me.txtCargosTotal.Text = CargosTotal.ToString(" ##,##0.00")
        Me.txtSaldoTotalNeto.Text = SaldoTotalNeto.ToString(" ##,##0.00")
        Me.txtMontoSolicitado.Text = MontoSolicitado
    End Sub

    Private Sub FondoSaldo_PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
        CargaTotales()
    End Sub

    Private Sub btnLimpiarPS_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnLimpiarPS.Click
        InicioTabPartidaSolicitud()
    End Sub

    Private Sub btnGuardarPS_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnGuardarPS.Click
        Mouse.OverrideCursor = Cursors.Wait
            If ValidarPartidaSolicitud() Then
                If MessageBox.Show ("¿Desea guardar la partida?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
                    Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.CREAR_PS, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnGuardarPS")
                    Try
                        If BSO.EstatusBSOID = CTBSO.enumEstatusBSO.NO_CREADA Then
                            BSO.EstatusBSOID = CTBSO.enumEstatusBSO.ABIERTA
                            BSO.CrearBSO(eventoBit)
                        End If
                        Dim TVNg As CTCatalogoGenerico = Me.cmbTipoSolicitud.SelectedItem
                        Dim partida As CTPartidaSolicitud = New CTPartidaSolicitud()
                        partida.TvngId = TVNg.Clave
                        partida.FecSol = DateTime.Parse(BSO.FecOperacion.ToString("dd/MM/yyyy") & " " & Date.Now.ToString("hh:mm:ss"))
                        partida.FecLiq = DateTime.Parse(Me.dtpFechaLiquidacion.Text.ToString() & " " & Date.Now.ToString("hh:mm:ss"))
                        partida.FecPlazo = BSO.FecOperacion.AddDays(Integer.Parse(Me.txtPlazo.Text))
                        partida.CantidadSol = Double.Parse(Me.txtMontoSolicitado.Text)
                        partida.Comentarios = Me.txtComentario.Text
                        partida.TipoCantidadId = CTPartidaSolicitud.enumTipoCantidad.EFECTIVO
                        If TVNg.Clave.ToString().StartsWith("BAN") Then
                            partida.TipoCantidadId = CTPartidaSolicitud.enumTipoCantidad.TITULOS
                        End If
                        partida.Plazo = Integer.Parse(Me.txtPlazo.Text)
                        partida.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.CREADA
                        
                        BSO.AgregarPS(partida, eventoBit)
                        eventoBit.ActualizarFechaFinDelEvento()
                        servicioGenerales.RegistrarNotificacion("Partida Solicitud - Se crea partida de solicitud")
                        MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                        InicioTabPartidaSolicitud()
                    Catch ex As Exception
                        eventoBit.ActualizarFechaFinDelEvento()
                        Mouse.OverrideCursor = Nothing
                        MessageBox.Show("Error TI500. Error al guardar partida de solicitud: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                    End Try
                    
                    
                End If
                
            End If

        Mouse.OverrideCursor = Nothing
    End Sub

    Private Function ValidarPartidaSolicitud() As Boolean
        
        Dim FechaLiq = DateTime.Parse(Me.dtpFechaLiquidacion.Text)

        Dim fechaHabil As Boolean = ValidaFechaHabil(FechaLiq)
        
        If Not fechaHabil Then
            MessageBox.Show("La fecha liquidación no puede ser un día inhabil.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If FechaLiq < Datetime.Parse(BSO.FecOperacion.ToString("dd/MM/yyyy")) Then
            MessageBox.Show("La fecha liquidación no puede ser menor a la fecha solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        
        If Me.cmbTipoSolicitud.SelectedIndex < 0 Then
            MessageBox.Show("Debe seleccionar un tipo de solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If Integer.Parse(Me.txtPlazo.Text) < 1 Then
            MessageBox.Show("El plazo debe ser mayor a cero.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        fechaHabil = ValidaFechaHabil(BSO.FecOperacion.AddDays(Integer.Parse(Me.txtPlazo.Text)))
        If Not fechaHabil Then
            MessageBox.Show("El plazo no puede caer en día inhabil.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If
            
        If Double.Parse(Me.txtMontoSolicitado.Text) < 0.01 Then
            MessageBox.Show("El monto solicitado debe ser mayor a cero.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If Me.grdSaldoFondos.Items.Count = 0 Then
            MessageBox.Show("El tipo de solicitud debe tener al menos un fondo asociado.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        Return True

        
    End Function

#End Region

#Region "TAB PARTIDA AJUSTE"

    Private Sub InicioTabPartidaAjuste()
        partida = New CTPartidaSolicitud()
        If ValidarSeleccionPS() = True Then
            InicioPartidaAjuste()
        Else
            Me.tabContenedor.SelectedIndex = 0
            Me.tabContenedor.SelectedItem = Me.tabResumenBSO
            Me.tabResumenBSO.IsSelected = True
        End If

        
    End Sub
    

    Private Sub grdRemanentesInter_CellEditEnding(ByVal sender As System.Object, ByVal e As System.Windows.Controls.DataGridCellEditEndingEventArgs) Handles grdRemanentesInter.CellEditEnding
        Dim txtContenidoCelda As Object
        Try
            If e.EditAction <> DataGridEditAction.Cancel Then
                If Not sender.CurrentItem Is Nothing Then
                    txtContenidoCelda = sender.Columns(e.Column.DisplayIndex).GetCellContent(CType(e.Row.Item, CTRemanentesInterPA))

                    If Not IsNumeric(txtContenidoCelda.Text) Then
                        CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                    Else
                        If Convert.ToDouble(txtContenidoCelda.Text) < 0 Then
                            CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                        End If
                    End If
                        
                Else
                    CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                End If
            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Sub InicioPartidaAjuste()
        Try
            Me.dtpFechaSolicitudA.Text = partida.FecSol.ToString("dd/MM/yyyy")
            Me.dtpFechaLiquidacionA.Text = partida.FecLiq.ToString("dd/MM/yyyy")
            Me.cmbTipoSolicitudA.ItemsSource = lstCatalogoTVNG
            Dim catalogo As CTCatalogoGenerico = lstCatalogoTVNG.Find(Function(x) x.Valor = partida.Tvng)
            Me.cmbTipoSolicitudA.SelectedItem = catalogo
            Me.txtPlazoA.Text = partida.Plazo
            Dim lstPEJ As List(Of CTPartidaEJ)
            lstPEJ = partida.LstPEJ.FindAll(Function(x) x.Flexible = True _
                                And (x.EstatusPEJ.EstatusDist = CTEstadoPEJ.enumEstatusPEJ.PARCIAL Or x.EstatusPEJ.EstatusDist = CTEstadoPEJ.enumEstatusPEJ.POR_DISTRIBUIR))
            lstRemanenteInter = New List(Of CTRemanentesInterPA) ()
            Dim remanente As CTRemanentesInterPA
            Dim totalAjusteTemp As Double = 0
            Dim ajusteTemp As Double = 0
            For Each pEjecucion In lstPEJ
                remanente = New CTRemanentesInterPA()
                remanente.IdPEJ = pEjecucion.PartidaEJId
                remanente.IdIntermediario = pEjecucion.Intermediario.IdIntermediario
                remanente.Intermediario = pEjecucion.Intermediario.NombreIntermediario
                ajusteTemp = SumaAjustes(pEjecucion)
                totalAjusteTemp = totalAjusteTemp + ajusteTemp
                remanente.Remanente =  pEjecucion.EstatusPEJ.CantidadXDistrib + ajusteTemp
                remanente.Cargos = 0
                AddHandler remanente.PropertyChanged, AddressOf Remante_PropertyChanged
                If remanente.Remanente > 0 then
                    lstRemanenteInter.Add(remanente)
                End If
                
            Next
            Me.grdRemanentesInter.ItemsSource = lstRemanenteInter
            Me.txtMontoSolicitadoA.Text = partida.CantidadSol.ToString(" ##,##0.00")
            Me.txtMontoActualA.Text = (partida.CantidadActualPS + totalAjusteTemp).ToString(" ##,##0.00")
            Me.txtComentarioA.Clear()
            Me.txtTotalAjuste.Clear()
        Catch ex As Exception
            Mouse.OverrideCursor = Nothing
            MessageBox.Show("Error TI500. Error al cargar datos iniciales para crear partida de ajuste: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
        

    End Sub

    Private Sub Remante_PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
        CargaTotalAjuste()
    End Sub

    Private Sub CargaTotalAjuste()
        Dim CargosTotal As Double = 0
        For Each remanenteInter As CTRemanentesInterPA In lstRemanenteInter
            CargosTotal = CargosTotal + remanenteInter.Cargos 
        Next
        Me.txtTotalAjuste.Text = CargosTotal.ToString(" ##,##0.00")
        
    End Sub

    Private Function SumaAjustes(ByRef pEjecucion As CTPartidaEJ) As Decimal
        Dim lstPAJ As List(Of CTPartidaAjuste)
        Dim suma As Decimal = 0
        lstPAJ = pEjecucion.LstPAJ.FindAll(Function(x) x.Estatus = CTPartidaAjuste.enumEstatusPAJ.CREADA Or x.Estatus = CTPartidaAjuste.enumEstatusPAJ.ENVIADA)
        For Each ajuste In lstPAJ
            suma = suma + ajuste.CantidadAjus
        Next
        Return suma
    End Function

    Private Function ValidarSeleccionPS() As Boolean

        Dim valido As Boolean = True
        Dim count As Integer = 0
        Dim lst = Me.grdResumenBSO.ItemsSource
        For Each resumen As CTPartidaSolicitud In lst
            If resumen.SeleccionSol = True Then
                count = count + 1
                partida = resumen
            End If
        Next
        If count = 0 Then
            MessageBox.Show ("Debe seleccionarse al menos una partida de solicitud para crear ajuste.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If count > 1 Then
            MessageBox.Show ("Solo debe seleccionarse una partida de solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If
        
        If valido and partida.TipoPartida = "Ajuste" Then
            MessageBox.Show ("Solo debe seleccionarse una partida de solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If valido and partida.TipoPartida = "Solicitud" and partida.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.EN_DISTRIBUCION _
            and partida.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.LIBERADA Then
            MessageBox.Show ("Solo debe seleccionarse una partida de solicitud con estatus de LIBERADA o EN DISTRIBUCIÓN.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If valido and partida.IntermFlexibles = 0 Then
            MessageBox.Show ("La partida de solicitud debe tener intermediarios con remanente.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        Return valido
    End Function

    Private Sub btnLimpiarPA_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnLimpiarPA.Click
        InicioPartidaAjuste()
    End Sub

    Private Sub btnGuardarPA_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnGuardarPA.Click
        Mouse.OverrideCursor = Cursors.Wait
        Dim valido = ValidarPartidaPA()
        If valido Then
            If MessageBox.Show ("¿Desea guardar la partida de ajuste?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
                Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.CREAR_PAJ, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnGuardarPA")
                Try
                    Dim ajuste As CTPartidaAjuste
                    For Each remanente As CTRemanentesInterPA In lstRemanenteInter
                        If remanente.Cargos > 0 then
                            ajuste = New CTPartidaAjuste()
                            ajuste.PartidaSolicitudId = partida.PartidaSolId
                            ajuste.PartidaEJId = remanente.IdPEJ
                            ajuste.TipoCantidadId = partida.TipoCantidadId
                            ajuste.FecAplicacion = Datetime.Parse(partida.FecSol.ToString("dd/MM/yyyy") & " " & Date.Now.ToString("hh:mm:ss"))
                            ajuste.Comentario = Me.txtComentarioA.Text
                            ajuste.CantidadAjus = remanente.Cargos
                            eventoBit.GuadarDetalle("PAJObj", ajuste.ToString())
                            BSO.AgregarPAJ(ajuste)
                        End If
                        
                    Next
                    eventoBit.ActualizarFechaFinDelEvento()
                    servicioGenerales.RegistrarNotificacion("Partida Ajuste - Se crea partida de ajuste")
                    MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                    ReinicioTab()
                Catch ex As Exception
                    Dim errorBit = eventoBit.GuardarError("Error al crear PAJ: " & ex.Message)
                    eventoBit.ActualizarFechaFinDelEvento()
                    MessageBox.Show("Error TI500. Error al guardar partida de ajuste: " & ex.Message & " -Codigo:" & errorBit.IdError, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                    Mouse.OverrideCursor = Nothing
                End Try
                
                
            End If
        End If
        Mouse.OverrideCursor = Nothing
    End Sub

    Private Function ValidarPartidaPA() As Boolean

        For Each remanente As CTRemanentesInterPA In lstRemanenteInter
            If remanente.Cargos > 0 then
                If remanente.Remanente < remanente.Cargos Then
                    MessageBox.Show ("No debe haber un cargo adicional mayor al remanente.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
                    Return False
                End If
            End If
                        
        Next

        If Me.txtTotalAjuste.Text = "" Then
            Me.txtTotalAjuste.Text = "0.00"
        End If

        If Double.Parse(Me.txtTotalAjuste.Text) < 0.01 Then
            MessageBox.Show ("Debe haber al menos un cargo a intermediario.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            Return False
        End If
        Return True
    End Function

#End Region

#Region "TAB MODIFICAR PARTIDA SOLICITUD"

    Private Sub InicioTabModificarPS()
        partida = New CTPartidaSolicitud()
        If ValidarSeleccionMPS() = True Then
            InicioModificarPS()
        Else
            ReinicioTab()
        End If

    End Sub

    Private Sub grdSaldoFondosMPS_CellEditEnding(ByVal sender As System.Object, ByVal e As System.Windows.Controls.DataGridCellEditEndingEventArgs) Handles grdSaldoFondosMPS.CellEditEnding
        Dim txtContenidoCelda As Object
        Try
            If e.EditAction <> DataGridEditAction.Cancel Then
                If Not sender.CurrentItem Is Nothing Then
                    txtContenidoCelda = sender.Columns(e.Column.DisplayIndex).GetCellContent(CType(e.Row.Item, CTSaldoFondosPS))

                    If Not IsNumeric(txtContenidoCelda.Text) Then
                        CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                    Else
                        If Convert.ToDouble(txtContenidoCelda.Text) < 0 Then
                            CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                        End If
                    End If
                        
                Else
                    CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                End If
            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Function ValidarSeleccionMPS() As Boolean

        Dim valido As Boolean = True
        Dim count As Integer = 0
        Dim lst = Me.grdResumenBSO.ItemsSource
        For Each resumen As CTPartidaSolicitud In lst
            If resumen.SeleccionSol = True Then
                count = count + 1
                partida = resumen
            End If
        Next
        If count = 0 Then
            MessageBox.Show ("Debe seleccionar al menos una partida de solicitud para modificarla.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If count > 1 Then
            MessageBox.Show ("Solo debe seleccionarse una partida de solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If valido and partida.TipoPartida = "Ajuste" Then
            MessageBox.Show ("Solo debe seleccionarse una partida de solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If valido and partida.TipoPartida = "Solicitud" _
                and (partida.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.CREADA And partida.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.ENVIADA) Then
            MessageBox.Show ("Solo debe seleccionarse una partida de solicitud con estatus de CREADA o ENVIADA.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        Return valido
    End Function

    Private Sub InicioModificarPS()
        Mouse.OverrideCursor = Cursors.Wait
        Try
            Me.dtpFechaSolicitudMPS.Text = partida.FecSol.ToString("dd/MM/yyyy")
            Me.dtpFechaLiquidacionMPS.Text = partida.FecLiq.ToString("dd/MM/yyyy")
            Me.cmbTipoSolicitudMPS.ItemsSource = lstCatalogoTVNG
            Dim catalogo As CTCatalogoGenerico = lstCatalogoTVNG.Find(Function(x) x.Valor = partida.Tvng)
            Me.cmbTipoSolicitudMPS.SelectedItem = catalogo
            Me.txtPlazoMPS.Text = partida.Plazo
            Dim strFecha = (DateTime.Parse(Me.dtpFechaSolicitudMPS.Text)).ToString("yyyy-MM-dd")
            lstFondosSaldos = servicioGL.ObtieneFondosSaldos(catalogo.IdCat, catalogo.Clave, strFecha)
            For Each fondo As CTSaldoFondosPS In lstFondosSaldos
                    AddHandler fondo.PropertyChanged, AddressOf FondoSaldoMPS_PropertyChanged
                Next

            Me.grdSaldoFondosMPS.ItemsSource = lstFondosSaldos
            Me.txtComentarioMPS.Text = partida.Comentarios
            CargaTotalesMPS()
            Me.txtMontoSolicitadoMPS.Text = partida.CantidadSol
        Catch ex As Exception
            Mouse.OverrideCursor = Nothing
            MessageBox.Show("Error TI500. Error al cargar datos iniciales para modificar partida de solicitud: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
        Mouse.OverrideCursor = Nothing
    End Sub
    
    Private Sub cmbTipoSolicitudMPS_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs)
        Mouse.OverrideCursor = Cursors.Wait
        Try
            Dim strFecha = (DateTime.Parse(Me.dtpFechaSolicitudMPS.Text)).ToString("yyyy-MM-dd")
            Dim tipoSolicitud As CTCatalogoGenerico
            tipoSolicitud = Me.cmbTipoSolicitudMPS.SelectedItem
            lstFondosSaldos = New List(Of CTSaldoFondosPS)
            If (tipoSolicitud IsNot Nothing) Then
                lstFondosSaldos = servicioGL.ObtieneFondosSaldos(tipoSolicitud.IdCat, tipoSolicitud.Clave, strFecha)
            
                For Each fondo As CTSaldoFondosPS In lstFondosSaldos
                    AddHandler fondo.PropertyChanged, AddressOf FondoSaldoMPS_PropertyChanged
                Next
            End If


            Me.grdSaldoFondosMPS.ItemsSource = lstFondosSaldos
            CargaTotalesMPS()
        Catch ex As Exception
            Mouse.OverrideCursor = Nothing
            MessageBox.Show("Error TI500. Error al cargar posicion de fondos: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
        Mouse.OverrideCursor = Nothing
    End Sub

    Private Sub CargaTotalesMPS()
        Dim SaldoTotal As Double
        Dim SaldoTotalSinCortos As Double
        Dim CargosTotal As Double
        Dim SaldoTotalNeto As Double
        Dim MontoSolicitado As Double

        SaldoTotal = 0
        SaldoTotalSinCortos = 0
        CargosTotal = 0
        For Each fondo As CTSaldoFondosPS In lstFondosSaldos
            SaldoTotal = SaldoTotal + fondo.Saldo
            If (fondo.Saldo > 0) Then
                SaldoTotalSinCortos = SaldoTotalSinCortos + fondo.Saldo

            End If
            CargosTotal = CargosTotal + fondo.Cargos
        Next

        SaldoTotalNeto = SaldoTotalSinCortos - CargosTotal
        MontoSolicitado = SaldoTotalNeto

        Me.txtSaldoTotalMPS.Text = SaldoTotal.ToString(" ##,##0.00")
        Me.txtSaldoTotalSinCortosMPS.Text = SaldoTotalSinCortos.ToString(" ##,##0.00")
        Me.txtCargosTotalMPS.Text = CargosTotal.ToString(" ##,##0.00")
        Me.txtSaldoTotalNetoMPS.Text = SaldoTotalNeto.ToString(" ##,##0.00")
        Me.txtMontoSolicitadoMPS.Text = MontoSolicitado
    End Sub

    Private Sub FondoSaldoMPS_PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
        CargaTotalesMPS()
    End Sub

    Private Sub btnLimpiarMPS_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnLimpiarMPS.Click
        InicioModificarPS()
    End Sub

    Private Sub btnGuardarMPS_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnGuardarMPS.Click
        Mouse.OverrideCursor = Cursors.Wait
        
        If ValidarPartidaSolicitudMPS() Then
            If MessageBox.Show ("¿Desea actualizar la partida de solicitud?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
        
                Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.MODIFICAR_PS, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnGuardarMPS")
                Try
                    Dim TVNg As CTCatalogoGenerico = Me.cmbTipoSolicitudMPS.SelectedItem
                    partida.TvngId = TVNg.Clave
                    partida.FecSol = Datetime.Parse(BSO.FecOperacion.ToString("dd/MM/yyyy") & " " & Date.Now.ToString("hh:mm:ss"))
                    partida.FecLiq = DateTime.Parse(Me.dtpFechaLiquidacionMPS.Text.ToString() & " " & Date.Now.ToString("hh:mm:ss"))
                    partida.FecPlazo = BSO.FecOperacion.AddDays(Integer.Parse(Me.txtPlazoMPS.Text))
                    partida.CantidadSol = Double.Parse(Me.txtMontoSolicitadoMPS.Text)
                    partida.Comentarios = Me.txtComentarioMPS.Text
                    partida.TipoCantidadId = CTPartidaSolicitud.enumTipoCantidad.EFECTIVO
                    If TVNg.Clave.ToString().StartsWith("BAN") Then
                        partida.TipoCantidadId = CTPartidaSolicitud.enumTipoCantidad.TITULOS
                    End If
                    partida.Plazo = Integer.Parse(Me.txtPlazoMPS.Text)
                    partida.EstatusActualPS = CTPartidaSolicitud.enumEstatusPS.CREADA
                    eventoBit.GuadarDetalle("idPS", partida.PartidaSolId)
                    eventoBit.GuadarDetalle("PSObj", partida.ToString())
                    BSO.ModificarPS(partida)
                    eventoBit.ActualizarFechaFinDelEvento()
                    servicioGenerales.RegistrarNotificacion("Partida Solicitud - Se actualiza partida de solicitud")
                    MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                    ReinicioTab()
                Catch ex As Exception
                    Dim errorBit = eventoBit.GuardarError("Error al modificar PS: " & ex.Message)
                    eventoBit.ActualizarFechaFinDelEvento()
                    Mouse.OverrideCursor = Nothing
                    MessageBox.Show("Error TI500. Error al actualizar partida de solicitud: " & ex.Message & " -Codigo:" & errorBit.IdError, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                End Try
                    
            End If
                
        End If
        Mouse.OverrideCursor = Nothing
        
    End Sub

    Private Function ValidarPartidaSolicitudMPS() As Boolean
        
        Dim FechaLiq = DateTime.Parse(Me.dtpFechaLiquidacionMPS.Text)

        Dim fechaHabil As Boolean = ValidaFechaHabil(FechaLiq)
        
        If Not fechaHabil Then
            MessageBox.Show("La fecha liquidación no puede ser un día inhabil.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If FechaLiq < Datetime.Parse(BSO.FecOperacion.ToString("dd/MM/yyyy")) Then
            MessageBox.Show("La fecha liquidación no puede ser menor a la fecha solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If Me.cmbTipoSolicitudMPS.SelectedIndex < 0 Then
            MessageBox.Show("Debe seleccionar un tipo de solicitud.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If Integer.Parse(Me.txtPlazoMPS.Text) < 1 Then
            MessageBox.Show("El plazo debe ser mayor a cero.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        fechaHabil = ValidaFechaHabil(BSO.FecOperacion.AddDays(Integer.Parse(Me.txtPlazoMPS.Text)))
        If Not fechaHabil Then
            MessageBox.Show("El plazo no puede caer en día inhabil.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If Double.Parse(Me.txtMontoSolicitadoMPS.Text) < 0.01 Then
            MessageBox.Show("El monto solicitado debe ser mayor a cero.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        If Me.grdSaldoFondosMPS.Items.Count = 0 Then
            MessageBox.Show("El tipo de solicitud debe tener al menos un fondo asociado.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation)
            Return False
        End If

        Return True
    End Function


#End Region
    
#Region "TAB MODIFICAR PARTIDA AJUSTE"

    Private Sub InicioTabModificarPA()
        partida = New CTPartidaSolicitud()
        If ValidarSeleccionMPA() = True Then
            InicioModificarPA()
        Else
            ReinicioTab()
        End If

        
    End Sub
    
    Private Sub grdRemanentesInterMPA_CellEditEnding(ByVal sender As System.Object, ByVal e As System.Windows.Controls.DataGridCellEditEndingEventArgs) Handles grdRemanentesInterMPA.CellEditEnding
        Dim txtContenidoCelda As Object
        Try
            If e.EditAction <> DataGridEditAction.Cancel Then
                If Not sender.CurrentItem Is Nothing Then
                    txtContenidoCelda = sender.Columns(e.Column.DisplayIndex).GetCellContent(CType(e.Row.Item, CTRemanentesInterPA))

                    If Not IsNumeric(txtContenidoCelda.Text) Then
                        CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                    Else
                        If Convert.ToDouble(txtContenidoCelda.Text) < 0 Then
                            CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                        End If
                    End If
                        
                Else
                    CType(sender, DataGrid).CancelEdit(DataGridEditingUnit.Cell)
                End If
            End If
        Catch ex As Exception

        End Try
    End Sub

    Private Sub InicioModificarPA()
        Mouse.OverrideCursor = Cursors.Wait
        Try
            Me.dtpFechaSolicitudMPA.Text = partida.FecSol.ToString("dd/MM/yyyy")
            Me.dtpFechaLiquidacionMPA.Text = partida.FecLiq.ToString("dd/MM/yyyy")
            Me.cmbTipoSolicitudMPA.ItemsSource = lstCatalogoTVNG
            Dim catalogo As CTCatalogoGenerico = lstCatalogoTVNG.Find(Function(x) x.Valor = partida.Tvng)
            Me.cmbTipoSolicitudMPA.SelectedItem = catalogo
            Me.txtPlazoMPA.Text = partida.Plazo

            Dim ps As CTPartidaSolicitud = BSO.LstPS.Find(Function(x) x.PartidaSolId = partida.AncestroPS)
            Dim lstPEJ As List(Of CTPartidaEJ)
            lstPEJ = ps.LstPEJ.FindAll(Function(x) x.Flexible = True _
                                And (x.EstatusPEJ.EstatusDist = CTEstadoPEJ.enumEstatusPEJ.PARCIAL Or x.EstatusPEJ.EstatusDist = CTEstadoPEJ.enumEstatusPEJ.POR_DISTRIBUIR))
            lstRemanenteInter = New List(Of CTRemanentesInterPA) ()
            Dim remanente As CTRemanentesInterPA
            Dim totalAjusteTemp As Double = 0
            Dim ajusteTemp As Double = 0
            For Each pEjecucion In lstPEJ
                Dim paj As CTPartidaAjuste = pEjecucion.LstPAJ.Find(Function(x) x.PartidaAjusteID = partida.PartidaSolId)
                ajusteTemp = SumaAjustes(pEjecucion)
                totalAjusteTemp = totalAjusteTemp + ajusteTemp
                If paj IsNot Nothing Then
                    remanente = New CTRemanentesInterPA()
                    remanente.IdPEJ = pEjecucion.PartidaEJId
                    remanente.IdIntermediario = pEjecucion.Intermediario.IdIntermediario
                    remanente.Intermediario = pEjecucion.Intermediario.NombreIntermediario
                    totalAjusteTemp = totalAjusteTemp - paj.CantidadAjus
                    remanente.Remanente =  pEjecucion.EstatusPEJ.CantidadXDistrib + ajusteTemp
                    remanente.Remanente = remanente.Remanente - paj.CantidadAjus
                    remanente.Cargos = paj.CantidadAjus * -1
                
                    AddHandler remanente.PropertyChanged, AddressOf RemanteMPA_PropertyChanged
                    lstRemanenteInter.Add(remanente)
                End If

            Next
            Me.grdRemanentesInterMPA.ItemsSource = lstRemanenteInter


            Dim solicitud As CTPartidaSolicitud = BSO.LstPS.Find(Function(x) x.PartidaSolId = partida.PartidaSolId)
            Me.txtMontoSolicitadoMPA.Text = ps.CantidadSol.ToString(" ##,##0.00")
            Me.txtMontoActualMPA.Text = (ps.CantidadActualPS + totalAjusteTemp).ToString(" ##,##0.00")
            Me.txtComentarioMPA.Text = partida.Comentarios
            Me.txtTotalAjusteMPA.Text = (partida.CantidadSol * -1).ToString(" ##,##0.00")
        
        Catch ex As Exception
            Mouse.OverrideCursor = Nothing
            MessageBox.Show("Error TI500. Error al cargar datos iniciales para modificar partida de ajuste: " & ex.Message, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
        End Try
        Mouse.OverrideCursor = Nothing

        
    End Sub

    Private Function ValidarSeleccionMPA() As Boolean

        Dim valido As Boolean = True
        Dim count As Integer = 0
        Dim lst = Me.grdResumenBSO.ItemsSource
        For Each resumen As CTPartidaSolicitud In lst
            If resumen.SeleccionSol = True Then
                count = count + 1
                partida = resumen
            End If
        Next
        If count = 0 Then
            MessageBox.Show ("Debe seleccionar al menos una partida de ajuste para modificarla.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If count > 1 Then
            MessageBox.Show ("Solo debe seleccionarse una partida de ajuste.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If valido and partida.TipoPartida = "Solicitud" Then
            MessageBox.Show ("Solo debe seleccionarse una partida de ajuste.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        If valido and partida.TipoPartida = "Ajuste" and partida.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.CREADA _
            and partida.EstatusActualPS <> CTPartidaSolicitud.enumEstatusPS.ENVIADA Then
            MessageBox.Show ("Solo debe seleccionarse una partida de ajuste con estatus de CREADA o ENVIADA.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            valido = False
        End If

        Return valido
    End Function

    Private Sub RemanteMPA_PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
        CargaTotalAjusteMPA()
    End Sub

    Private Sub CargaTotalAjusteMPA()
        Dim CargosTotal As Double = 0
        For Each remanenteInter As CTRemanentesInterPA In lstRemanenteInter
            CargosTotal = CargosTotal + remanenteInter.Cargos 
        Next
        Me.txtTotalAjusteMPA.Text = CargosTotal.ToString(" ##,##0.00")
        
    End Sub

    Private Sub btnLimpiarMPA_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnLimpiarMPA.Click
        InicioModificarPA()
    End Sub

    Private Sub btnGuardarMPA_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnGuardarMPA.Click
        Mouse.OverrideCursor = Cursors.Wait

        Dim valido = ValidarPartidaMPA()
        If valido Then
            If MessageBox.Show ("¿Desea actualizar la partida de ajuste?", tituloMensaje, MessageBoxButton.YesNo, MessageBoxImage.Question)  = MessageBoxResult.Yes Then
                Dim eventoBit = CBitacoraFRIDA.GenerarEvento(CBitacoraFRIDA.CveEventosBit.MODIFICAR_PAJ, CServiciosMacrosSyBaseDB.IDUSUARIO, "FLResumenBSO.xaml -> btnGuardarMPA")
                Try
                    Dim ajuste As CTPartidaAjuste
                    For Each remanente As CTRemanentesInterPA In lstRemanenteInter
                        If remanente.Cargos > 0 then
                            ajuste = New CTPartidaAjuste()
                            ajuste.PartidaAjusteID = partida.PartidaSolId
                            ajuste.CantidadAjus = remanente.Cargos
                            eventoBit.GuadarDetalle("idPAJ", ajuste.PartidaAjusteID)
                            eventoBit.GuadarDetalle("CantidadAjus", ajuste.CantidadAjus)
                            BSO.ModificarPAJ(ajuste)
                        End If
                    Next
                    eventoBit.ActualizarFechaFinDelEvento()
                    servicioGenerales.RegistrarNotificacion("Partida Ajuste - Se actualiza partida de ajuste")
                    MessageBox.Show("Operación Exitosa" , tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Information)
                    ReinicioTab()
                Catch ex As Exception
                    Dim errorBit = eventoBit.GuardarError("Error al modificar PAJ: " & ex.Message)
                    eventoBit.ActualizarFechaFinDelEvento()
                    Mouse.OverrideCursor = Nothing
                    MessageBox.Show("Error TI500. Error al actualizar partida de ajuste: " & ex.Message & " -Codigo:" & errorBit.IdError, tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Error)
                End Try
                    
            End If
        End If

        Mouse.OverrideCursor = Nothing
    End Sub

    Private Function ValidarPartidaMPA() As Boolean

        For Each remanente As CTRemanentesInterPA In lstRemanenteInter
            If remanente.Cargos > 0 then
                If remanente.Remanente < remanente.Cargos Then
                    MessageBox.Show ("No debe haber un cargo adicional mayor al remanente.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
                    Return False
                End If
            End If
                        
        Next

        If Me.txtTotalAjusteMPA.Text = "" Then
            Me.txtTotalAjusteMPA.Text = "0.00"
        End If

        If Double.Parse(Me.txtTotalAjusteMPA.Text) < 0.01 Then
            MessageBox.Show ("Debe haber al menos un cargo a intermediario.", tituloMensaje, MessageBoxButton.OK, MessageBoxImage.Exclamation) 
            Return False
        End If
        Return True
    End Function
#End Region

    Private Function ValidaFechaHabil(ByVal fecha As Date) As Boolean
        For Each fechain As CFechaNoLab In fechasInhabiles
            If fechain.Fechanolab.ToString("dd/MM/yyyy") = fecha.ToString("dd/MM/yyyy")
                Return False
            End If
        Next
        If fecha.DayOfWeek() = DayOfWeek.Saturday or fecha.DayOfWeek() = DayOfWeek.Sunday Then
            Return False
        End If
            
        Return True
    End Function
 
    Private Function GetNumSiguienteDiaHabil(ByVal fecha As DateTime) As Integer
        Dim flag As Boolean = False
        Dim count As Integer = 0
        While Not flag
            fecha = fecha.AddDays(1)
            flag = ValidaFechaHabil(fecha)
            count = count + 1
        End While
        Return count
    End Function

End Class

#Region "CLASES DE VALIDACION"



Public Class NumericConverter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If value Is Nothing Then
            Return Nothing
        ElseIf targetType.UnderlyingSystemType = GetType([String]) Then
            Return value.ToString()
        Else
            Return value
        End If

    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New Exception

        If value Is Nothing Then
            Return Nothing
        Else

            If (New HashSet(Of Type)({GetType([Byte]), _
                                                      GetType([Decimal]), _
                                                      GetType([Double]), _
                                                      GetType(Int16), _
                                                      GetType(Int32), _
                                                      GetType(Int64), _
                                                      GetType([SByte]), _
                                                      GetType([Single]), _
                                                      GetType(UInt16), _
                                                      GetType(UInt32), _
                                                      GetType(UInt64)})).Contains(targetType.UnderlyingSystemType) Then

                If (New HashSet(Of Type)({GetType([Byte]), _
                                                      GetType(Int16), _
                                                      GetType(Int32), _
                                                      GetType(Int64), _
                                                      GetType([SByte]), _
                                                      GetType(UInt16), _
                                                      GetType(UInt32), _
                                                      GetType(UInt64)})).Contains(targetType.UnderlyingSystemType) Then
                    Try
                        Return Int32.Parse(value.ToString().Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty))
                    Catch
                        Return Nothing
                    End Try
                Else
                    Try
                        Return [Double].Parse(value.ToString().Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty))
                    Catch
                        Return Nothing
                    End Try
                End If
            Else
                Return value
            End If
        End If

    End Function

End Class

Public Class NumericToValueTypeConverter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If value Is Nothing Then
	        Return ValueTypes.NoNumeric
        Else
	        Dim numericTypes As New HashSet(Of Type)({GetType([Byte]), _
                                                      GetType([Decimal]), _
                                                      GetType([Double]), _
                                                      GetType(Int16), _
                                                      GetType(Int32), _
                                                      GetType(Int64), _
                                                      GetType([SByte]), _
                                                      GetType([Single]), _
                                                      GetType(UInt16), _
                                                      GetType(UInt32), _
                                                      GetType(UInt64)})

            If numericTypes.Contains(value.[GetType]()) Then
                Dim intTypes As New HashSet(Of Type)({GetType([Byte]), _
                                                      GetType(Int16), _
                                                      GetType(Int32), _
                                                      GetType(Int64), _
                                                      GetType([SByte]), _
                                                      GetType(UInt16), _
                                                      GetType(UInt32), _
                                                      GetType(UInt64)})
                If intTypes.Contains(value.[GetType]()) Then
                    Return ValueTypes.[Integer]
                Else
                    Return ValueTypes.[Double]
                End If
            Else
                Return ValueTypes.NoNumeric
            End If
        End If

    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New NotSupportedException()
    End Function

End Class

Public Class TextBoxMaskBehavior

#Region "MinimumValue Property"

    Public Shared Function GetMinimumValue(obj As DependencyObject) As Double
        Return CDbl(obj.GetValue(MinimumValueProperty))
    End Function

    Public Shared Sub SetMinimumValue(obj As DependencyObject, value As Double)
        obj.SetValue(MinimumValueProperty, value)
    End Sub

    Public Shared ReadOnly MinimumValueProperty As DependencyProperty =
        DependencyProperty.RegisterAttached("MinimumValue", _
                                            GetType(Double), _
                                            GetType(TextBoxMaskBehavior), _
                                            New FrameworkPropertyMetadata(Double.NaN, _
                                                                          AddressOf MinimumValueChangedCallback))

    Private Shared Sub MinimumValueChangedCallback(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim _this As TextBox = TryCast(d, TextBox)

        Dim vt As ValueTypes = GetValueType(_this)
        Dim min As Double = GetMinimumValue(_this)
        If Not min.Equals(Double.NaN) Then
            Select Case vt
                Case ValueTypes.[Integer]
                    If min < Convert.ToDouble(Int32.MinValue) Then
                        Throw New ArgumentOutOfRangeException("Overflow, minimum: " + Int32.MinValue.ToString())
                    End If
                    'SetMinimumValue(_this, Convert.ToDouble(Int32.MinValue));
                    Exit Select

                Case ValueTypes.[Double]
                    'Egy karakterrel előbb megállunk, hogy ne okozzon exception-t.
                    If min < ([Double].MinValue / 100) Then
                        Throw New ArgumentOutOfRangeException("Overflow, minimum: " + _
                                                              ([Double].MinValue / 100).ToString())
                    End If
                    'SetMinimumValue(_this, (Double.MinValue / 100));
                    Exit Select
            End Select
        End If

        ValidateTextBox(_this)
    End Sub
#End Region

#Region "MaximumValue Property"

    Public Shared Function GetMaximumValue(obj As DependencyObject) As Double
        Return CDbl(obj.GetValue(MaximumValueProperty))
    End Function

    Public Shared Sub SetMaximumValue(obj As DependencyObject, value As Double)
        obj.SetValue(MaximumValueProperty, value)
    End Sub

    Public Shared ReadOnly MaximumValueProperty As DependencyProperty = _
        DependencyProperty.RegisterAttached("MaximumValue", _
                                            GetType(Double), _
                                            GetType(TextBoxMaskBehavior), _
                                            New FrameworkPropertyMetadata(Double.NaN, _
                                                                          AddressOf MaximumValueChangedCallback))

    Private Shared Sub MaximumValueChangedCallback(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim _this As TextBox = TryCast(d, TextBox)

        Dim vt As ValueTypes = GetValueType(_this)
        Dim max As Double = GetMaximumValue(_this)
        If Not max.Equals(Double.NaN) Then
            Select Case vt
                Case ValueTypes.[Integer]
                    If max > Convert.ToDouble(Int32.MaxValue) Then
                        Throw New ArgumentOutOfRangeException("Overflow, maximum: " + Int32.MaxValue.ToString())
                    End If
                    'SetMinimumValue(_this, Convert.ToDouble(Int32.MinValue));
                    Exit Select

                Case ValueTypes.[Double]
                    'We stop two characters ahead, so as not to cause an exception.
                    If max > ([Double].MinValue / 100) Then
                        Throw New ArgumentOutOfRangeException("Overflow, maximum: " + _
                                                              ([Double].MaxValue / 100).ToString())
                    End If
                    'SetMinimumValue(_this, (Double.MinValue / 100));
                    Exit Select
            End Select
        End If

        ValidateTextBox(_this)
    End Sub
#End Region

#Region "Mask Property"

    Public Shared Function GetMask(obj As DependencyObject) As String
        Return DirectCast(obj.GetValue(MaskProperty), String)
    End Function

    Public Shared Sub SetMask(obj As DependencyObject, value As String)
        obj.SetValue(MaskProperty, value)
    End Sub

    Public Shared ReadOnly MaskProperty As DependencyProperty = _
        DependencyProperty.RegisterAttached("Mask", _
                                            GetType(String), _
                                            GetType(TextBoxMaskBehavior), _
                                            New FrameworkPropertyMetadata(AddressOf MaskChangedCallback))

    Private Shared Sub MaskChangedCallback(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        If TypeOf e.OldValue Is TextBox Then

            RemoveHandler TryCast(e.OldValue, TextBox).PreviewTextInput, AddressOf TextBox_PreviewTextInput
            RemoveHandler TryCast(e.OldValue, TextBox).TextChanged, AddressOf TextBox_TextChanged
            RemoveHandler TryCast(e.OldValue, TextBox).PreviewKeyDown, AddressOf TextBox_PreviewKeyDown
            RemoveHandler TryCast(e.OldValue, TextBox).GotKeyboardFocus, AddressOf _TextBox_GotKeyboardFocus

            DataObject.RemovePastingHandler(TryCast(e.OldValue, TextBox), _
                                            DirectCast(AddressOf TextBoxPastingEventHandler,  _
                                                       DataObjectPastingEventHandler))
        End If

        Dim _this As TextBox = TryCast(d, TextBox)
        If _this Is Nothing Then
            Return
        End If

        If String.Empty <> DirectCast(e.NewValue, String) Then
            AddHandler _this.PreviewTextInput, AddressOf TextBox_PreviewTextInput
            AddHandler _this.TextChanged, AddressOf TextBox_TextChanged
            AddHandler _this.PreviewKeyDown, AddressOf TextBox_PreviewKeyDown
            AddHandler _this.GotKeyboardFocus, AddressOf _TextBox_GotKeyboardFocus
            DataObject.AddPastingHandler(_this, DirectCast(AddressOf TextBoxPastingEventHandler,  _
                                                           DataObjectPastingEventHandler))
        End If

        ValidateTextBox(_this)
    End Sub

#End Region

#Region "ValueType Property"

    Public Shared Function GetValueType(obj As DependencyObject) As ValueTypes
        Return DirectCast(obj.GetValue(ValueTypeProperty), ValueTypes)
    End Function

    Public Shared Sub SetValueType(obj As DependencyObject, value As ValueTypes)
        obj.SetValue(ValueTypeProperty, value)
    End Sub

    Public Shared ReadOnly ValueTypeProperty As DependencyProperty = _
        DependencyProperty.RegisterAttached("ValueType", _
                                            GetType(ValueTypes), _
                                            GetType(TextBoxMaskBehavior), _
                                            New FrameworkPropertyMetadata(AddressOf ValueTypeChangedCallback))

    Private Shared Sub ValueTypeChangedCallback(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim _this As TextBox = TryCast(d, TextBox)
        ValidateTextBox(_this)
    End Sub
#End Region

#Region "Static Methods"

    Private Shared Sub _TextBox_GotKeyboardFocus(sender As Object, e As KeyboardFocusChangedEventArgs)
        Dim _this As TextBox = TryCast(sender, TextBox)
        'A tizedesvessző baloldalára áll kezdéskor
        If _this.Text.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) Then
            _this.CaretIndex = _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)
        Else
            _this.CaretIndex = _this.Text.Length
        End If
    End Sub

    Private Shared Sub TextBox_PreviewKeyDown(sender As Object, e As System.Windows.Input.KeyEventArgs)
        If (ValueTypes.NoNumeric <> GetValueType(TryCast(sender, TextBox))) AndAlso (Key.Space = e.Key) Then
            'Space is not allowed at number type entry.
            e.Handled = True
            Return
        End If

        If Key.Back = e.Key Then
            'Backspace
            Dim _this As TextBox = TryCast(sender, TextBox)
            If (0 = _this.SelectionLength) AndAlso (0 < _this.CaretIndex) Then
                'If nothing is selected, the cursor is not at the very beginning.
                If NumberFormatInfo.CurrentInfo.NumberDecimalSeparator = _this.Text.Substring(_this.CaretIndex - 1, 1) Then
                    'This does not have to be carried out if we want to delete the separator
                    _this.CaretIndex -= 1
                    e.Handled = True
                    Return
                End If

                If (True = _this.Text.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)) _
                    AndAlso (_this.CaretIndex > _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) + 1) Then
                    'If the cursor is at the decimal value and we delete backwards.
                    Dim caret As Integer = _this.CaretIndex
                    _this.Text = _this.Text.Substring(0, _this.CaretIndex - 1) + _this.Text.Substring(_this.CaretIndex) + "0"
                    _this.CaretIndex = caret - 1
                    e.Handled = True
                    Return
                End If
            End If

            If 0 < _this.CaretIndex Then
                If 0 < _this.SelectionLength Then
                    'If we delete the highlighted part of text.
                    Dim caret As Integer = _this.SelectionStart
                    Dim rcaret As Integer = _this.Text.Length - caret - _this.SelectionLength

                    Dim txtWS As String = _this.Text.Substring(0, caret)
                    Dim txtWOS As String = txtWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, _
                                                         [String].Empty)

                    Dim txtSWS As String = _this.Text.Substring(caret, _this.SelectionLength)
                    Dim txtSWOS As String = txtSWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, _
                                                           [String].Empty)

                    Dim text As String = _this.Text.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, _
                                                            [String].Empty)

                    'If the highlighted part contains the decimal separator, we put it back after deleting.
                    text = text.Substring(0, caret - (txtWS.Length - txtWOS.Length)) + _
                        (If(txtSWOS.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), _
                        NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, _
                        [String].Empty)) + _
                            text.Substring(caret - (txtWS.Length - txtWOS.Length) + _
                                           _this.SelectionLength - (txtSWS.Length - txtSWOS.Length))

                    _this.Text = text

                    If txtSWOS.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) Then
                        'If the decimal separator was also selected, then the cursor is put in front 
                        'of the decimal separator.
                        caret = _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)
                    Else
                        caret = _this.Text.Length - rcaret
                    End If

                    If caret < 0 Then
                        caret = 0
                    End If

                    _this.CaretIndex = caret
                    e.Handled = True
                    Return
                Else
                    'One item is deleted from the left.
                    Dim caret As Integer = _this.CaretIndex
                    Dim rcaret As Integer = _this.Text.Length - caret

                    Dim txtWS As String = _this.Text.Substring(0, caret)
                    Dim txtWOS As String = txtWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, _
                                                         [String].Empty)

                    Dim text As String = _this.Text.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, _
                                                            [String].Empty)

                    text = text.Substring(0, caret - (txtWS.Length - txtWOS.Length) - 1) + _
                        text.Substring(caret - (txtWS.Length - txtWOS.Length))

                    _this.Text = text

                    caret = _this.Text.Length - rcaret

                    If caret < 0 Then
                        caret = 0
                    End If

                    _this.CaretIndex = caret
                    e.Handled = True

                    Return
                End If

            End If
        End If

        If Key.Delete = e.Key Then
            'Del
            Dim _this As TextBox = TryCast(sender, TextBox)
            If (0 = _this.SelectionLength) AndAlso (_this.CaretIndex < _this.Text.Length) Then
                'If nothing is selected, the cursor is not at the very end.
                If NumberFormatInfo.CurrentInfo.NumberDecimalSeparator = _this.Text.Substring(_this.CaretIndex, 1) Then
                    'This does not have to be carried out if we want to delete the separator
                    _this.CaretIndex += 1
                    e.Handled = True
                    Return
                End If

                If (True = _this.Text.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)) _
                    AndAlso (_this.CaretIndex > _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)) Then
                    'If the cursor is at the decimal value and we delete.
                    Dim caret As Integer = _this.CaretIndex
                    Dim ind As Integer = _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)

                    _this.Text = _this.Text.Substring(0, caret) + _this.Text.Substring(caret + 1) + "0"
                    _this.CaretIndex = caret
                    e.Handled = True
                    Return
                End If
            End If

            If 0 < _this.SelectionLength Then
                'If we delete the highlighted part of text.
                Dim caret As Integer = _this.SelectionStart
                Dim rcaret As Integer = _this.Text.Length - caret - _this.SelectionLength

                Dim txtWS As String = _this.Text.Substring(0, caret)
                Dim txtWOS As String = txtWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                Dim txtSWS As String = _this.Text.Substring(caret, _this.SelectionLength)
                Dim txtSWOS As String = txtSWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                Dim text As String = _this.Text.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                'If the highlighted part contains the decimal separator, we put it back after deleting.
                text = text.Substring(0, caret - (txtWS.Length - txtWOS.Length)) + _
                    (If(txtSWOS.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), _
                    NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, _
                    [String].Empty)) + text.Substring(caret - (txtWS.Length - txtWOS.Length) + _this.SelectionLength - (txtSWS.Length - txtSWOS.Length))

                'If there is only one decimal separator, it will be deleted.
                text = (If(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator = text, [String].Empty, text))

                _this.Text = text

                If txtSWOS.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) Then
                    caret = _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)
                Else
                    caret = _this.Text.Length - rcaret
                End If

                If caret < 0 Then
                    caret = 0
                End If

                _this.CaretIndex = caret
                e.Handled = True
                Return
            Else
                If _this.CaretIndex < _this.Text.Length Then

                    'One item is deleted from the right.
                    Dim caret As Integer = _this.CaretIndex
                    Dim rcaret As Integer = _this.Text.Length - caret - 1

                    If NumberFormatInfo.CurrentInfo.NumberGroupSeparator = _this.Text.Substring(caret, 1) Then
                        rcaret -= 1
                    End If

                    Dim txtWS As String = _this.Text.Substring(0, caret)
                    Dim txtWOS As String = txtWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    Dim text As String = _this.Text.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    text = text.Substring(0, caret - (txtWS.Length - txtWOS.Length)) + text.Substring(caret - (txtWS.Length - txtWOS.Length) + 1)

                    _this.Text = text
                    caret = _this.Text.Length - rcaret

                    If caret < 0 Then
                        caret = 0
                    End If

                    _this.CaretIndex = caret
                    e.Handled = True
                    Return

                End If


            End If
        End If

        e.Handled = False
    End Sub

#End Region

#Region "Private Static Methods"

    Private Shared Sub TextBox_TextChanged(sender As Object, e As TextChangedEventArgs)
        Dim _this As TextBox = TryCast(sender, TextBox)

        Dim text As String = _this.Text.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

        Dim mask As String = GetMask(_this)
        Dim vt As ValueTypes = GetValueType(_this)

        If 0 <> mask.Length Then
            If 0 < _this.Text.Length Then
                If vt.Equals(ValueTypes.[Integer]) Then
                    'todo: TryParse/try-catch
                    _this.Text = [String].Format("{" + mask + "}", Int32.Parse(text))
                    e.Handled = True
                Else
                    _this.Text = [String].Format("{" + mask + "}", [Double].Parse(text))
                    e.Handled = True
                End If
            Else
                _this.Text = "0"
                e.Handled = True

            End If
        End If

    End Sub

    Private Shared Sub ValidateTextBox(_this As TextBox)
        If String.Empty <> GetMask(_this) Then
            _this.Text = ValidateValue(GetMask(_this), GetValueType(_this), _this.Text, GetMinimumValue(_this), GetMaximumValue(_this))
        End If
    End Sub

    Private Shared Sub TextBoxPastingEventHandler(sender As Object, e As DataObjectPastingEventArgs)
        Dim _this As TextBox = TryCast(sender, TextBox)
        Dim clipboard As String = TryCast(e.DataObject.GetData(GetType(String)), String)
        clipboard = ValidateValue(GetMask(_this), GetValueType(_this), clipboard, GetMinimumValue(_this), GetMaximumValue(_this))
        If Not String.IsNullOrEmpty(clipboard) Then
            _this.Text = clipboard
        End If
        e.CancelCommand()
        e.Handled = True
    End Sub

    Private Shared Sub TextBox_PreviewTextInput(sender As Object, e As System.Windows.Input.TextCompositionEventArgs)
        Dim _this As TextBox = TryCast(sender, TextBox)
        Dim isValid As Boolean = IsSymbolValid(GetMask(_this), e.Text, GetValueType(_this))
        Dim textInserted As Boolean = False
        Dim toNDS As Boolean = False

        If isValid Then
            'Current content
            Dim txtOld As String = _this.Text.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)
            'New content
            Dim txtNew As String = [String].Empty
            Dim handled As Boolean = False
            Dim caret As Integer = _this.CaretIndex
            Dim rcaret As Integer = 0

            If e.Text = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator Then
                'If we entered a decimal separator.
                Dim ind As Integer = _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) + 1
                rcaret = _this.Text.Length - ind
                'The text doesn't change.
                txtNew = txtOld
                handled = True
            End If

            If (Not handled) AndAlso (e.Text = NumberFormatInfo.CurrentInfo.NegativeSign) Then
                'We entered a negative symbol.
                If _this.Text.Contains(NumberFormatInfo.CurrentInfo.NegativeSign) Then
                    'A negative symbol is already in the text.
                    'As overriding the text initializes the cursor, the present position is remembered.
                    rcaret = _this.Text.Length - caret
                    txtNew = txtOld.Replace(NumberFormatInfo.CurrentInfo.NegativeSign, String.Empty)
                Else
                    'There is no negative symbol in the text.
                    'As overriding the text initializes the cursor, the present position is remembered.
                    rcaret = _this.Text.Length - caret
                    txtNew = NumberFormatInfo.CurrentInfo.NegativeSign + txtOld
                End If
                handled = True
            End If

            If Not handled Then
                textInserted = True
                If 0 < _this.SelectionLength Then
                    'We delete the highlighted text and insert what we have just written.
                    Dim ind As Integer = _this.SelectionStart
                    rcaret = _this.Text.Length - ind - _this.SelectionLength

                    Dim txtWS As String = _this.Text.Substring(0, ind)
                    Dim txtWOS As String = txtWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    Dim txtSWS As String = _this.Text.Substring(ind, _this.SelectionLength)
                    Dim txtSWOS As String = txtSWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    Dim txtNWS As String = e.Text
                    Dim txtNWOS As String = txtNWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    txtNew = txtOld.Substring(0, ind - (txtWS.Length - txtWOS.Length)) + txtNWOS + _
                        (If(txtSWOS.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator), _
                        NumberFormatInfo.CurrentInfo.NumberDecimalSeparator, _
                        [String].Empty)) + txtOld.Substring(ind - (txtWS.Length - txtWOS.Length) + _this.SelectionLength - (txtSWS.Length - txtSWOS.Length))

                    If txtSWOS.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) Then
                        'If the decimal separator was also highlighted, then the cursor is put in front of the decimal separator.
                        toNDS = True

                    End If
                Else
                    'We insert the character to the right of the cursor.
                    Dim ind As Integer = _this.CaretIndex
                    rcaret = _this.Text.Length - ind

                    If (0 < rcaret) AndAlso (NumberFormatInfo.CurrentInfo.NumberGroupSeparator = _this.Text.Substring(ind, 1)) Then
                        rcaret -= 1
                    End If

                    Dim txtWS As String = _this.Text.Substring(0, ind)
                    Dim txtWOS As String = txtWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    Dim txtNWS As String = e.Text
                    Dim txtNWOS As String = txtNWS.Replace(NumberFormatInfo.CurrentInfo.NumberGroupSeparator, [String].Empty)

                    txtNew = txtOld.Substring(0, ind - (txtWS.Length - txtWOS.Length)) + txtNWOS + txtOld.Substring(ind - (txtWS.Length - txtWOS.Length))
                End If
            End If

            Try
                Dim val As Double = [Double].Parse(txtNew)
                Dim newVal As Double = ValidateLimits(GetMinimumValue(_this), GetMaximumValue(_this), val, GetValueType(_this))
                If val <> newVal Then
                    txtNew = newVal.ToString()
                ElseIf val = 0 Then
                    If Not txtNew.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator) Then
                        txtNew = "0"
                    End If
                End If
            Catch
                txtNew = "0"
            End Try

            _this.Text = txtNew

            If (True = _this.Text.Contains(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)) _
                AndAlso (caret > _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)) Then
                'If the cursor is at the decimal value, then it moves to the right of the decimal separator, if possible.
                If caret < _this.Text.Length Then
                    If textInserted Then
                        caret += 1
                        rcaret = _this.Text.Length - caret
                    End If
                Else
                    'We are at the very end; it's not possible to enter more characters.
                    If textInserted Then
                        _this.Text = txtOld
                    End If
                End If
            End If

            caret = _this.Text.Length - rcaret

            If caret < 0 Then
                caret = 0
            End If

            If toNDS Then
                _this.CaretIndex = _this.Text.IndexOf(NumberFormatInfo.CurrentInfo.NumberDecimalSeparator)
            Else
                _this.CaretIndex = caret

            End If
        End If

        e.Handled = True
    End Sub

    Private Shared Function ValidateValue(mask As String, vt As ValueTypes, value As String, min As Double, max As Double) As String
        If String.IsNullOrEmpty(value) Then
            Return String.Empty
        End If

        value = value.Trim()

        Select Case vt
            Case ValueTypes.[Integer]
                Try
                    value = ValidateLimits(min, max, Int32.Parse(value), vt).ToString()
                    Return value
                Catch
                End Try
                Return String.Empty

            Case ValueTypes.[Double]
                Try
                    value = ValidateLimits(min, max, [Double].Parse(value), vt).ToString()
                    Return value
                Catch
                End Try
                Return String.Empty
        End Select
        Return String.Empty

    End Function

    Private Shared Function ValidateLimits(min As Double, max As Double, value As Double, vt As ValueTypes) As Double
        If Not min.Equals(Double.NaN) Then
            If value < min Then
                Return min
            End If
        Else
            Select Case vt
                Case ValueTypes.[Integer]
                    If value < Convert.ToDouble(Int32.MinValue) Then
                        Return Convert.ToDouble(Int32.MinValue)
                    End If
                    Exit Select

                Case ValueTypes.[Double]
                    'Két karakterrel előbb megállunk, hogy ne okozzon exception-t.
                    If value < ([Double].MinValue / 100) Then
                        Return ([Double].MinValue / 100)
                    End If
                    Exit Select
            End Select
        End If

        If Not max.Equals(Double.NaN) Then
            If value > max Then
                Dim aux() As String = value.ToString().Split(".")
                If aux.Length = 1 Then
                    Return Convert.ToInt32(aux(0).Substring(0, aux(0).Length - 1))
                End If
                
                Return max

            End If
        Else
            Select Case vt
                Case ValueTypes.[Integer]
                    If value > Convert.ToDouble(Int32.MaxValue) Then
                        Return Convert.ToDouble(Int32.MaxValue)
                    End If
                    Exit Select

                Case ValueTypes.[Double]
                    'We stop two characters ahead, so as not to cause an exception.
                    If value > ([Double].MaxValue / 100) Then
                        Return ([Double].MaxValue / 100)
                    End If
                    Exit Select
            End Select
        End If
        Return value
    End Function

    Private Shared Function IsSymbolValid(mask As String, str As String, typ As ValueTypes) As Boolean
        Select Case typ
            Case ValueTypes.NoNumeric
                Return True
            Case ValueTypes.[Integer]
                If str = NumberFormatInfo.CurrentInfo.NegativeSign Then
                    Return True
                End If
                Exit Select
            Case ValueTypes.[Double]
                If str = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator OrElse str = NumberFormatInfo.CurrentInfo.NegativeSign Then
                    Return True
                End If
                Exit Select
        End Select

        If typ.Equals(ValueTypes.[Integer]) OrElse typ.Equals(ValueTypes.[Double]) Then
            For Each ch As Char In str
                If Not [Char].IsDigit(ch) Then
                    Return False
                End If
            Next

            Return True
        End If

        Return False

    End Function

#End Region

End Class

Public Enum ValueTypes
    NoNumeric
    [Integer]
    [Double]
End Enum

Public Class TestDataModel
    Implements INotifyPropertyChanged

#Region "Public Properties"

    Private _DataInt As Integer = 15
    
    Public Property DataInt() As Integer
        Get
            Return _DataInt
        End Get
        Set(value As Integer)
            If _DataInt <> value Then
                _DataInt = value
                RaisePropertyChanged("DataInt")
            End If
        End Set
    End Property

    Private _DataDouble As Double = 25

    Public Property DataDouble() As Double
        Get
            Return _DataDouble
        End Get
        Set(value As Double)
            If _DataDouble <> value Then
                _DataDouble = value
                RaisePropertyChanged("DataDouble")
            End If
        End Set
    End Property

    Private _DataNoNumeric As String = ""
    Public Property DataNoNumeric() As String
        Get
            Return _DataNoNumeric
        End Get
        Set(value As String)
            If _DataNoNumeric <> value Then
                _DataNoNumeric = value
                RaisePropertyChanged("DataNoNumeric")
            End If
        End Set
    End Property

#End Region

#Region "PropertyChanged"

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private Sub RaisePropertyChanged(PropertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))

    End Sub

#End Region

End Class

Class CFormatoSolicitudAjuste
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
        If IsNumeric(value) Then
            Dim valor As Double = CType(value, Double)

            If valor < 0 Then
               Return Brushes.Red
            Else
               Return Brushes.Green
            End If

        Else
            Dim valor As String = CType(value, String)
            
            If (valor <> "Solicitud") Then
                Return Brushes.Red
            Else
               Return Brushes.Green
            End If
        End If

    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
        Throw New Exception
    End Function

End Class

#End Region
